package middleware

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"goblog/utils"
	"goblog/utils/errmsg"
	"net/http"
	"strings"
	"time"
)

var JwtKey = []byte(utils.JwtKey) //密钥的参数，byte类型的切片

type MyClaims struct { //需要一个结构体来接收参数
	Username           string `json:"username"` //必须和User里面定义的一致
	Password           string `json:"password"`
	jwt.StandardClaims        //嵌套一个自带的结构体
}

//生成token

func SetToken(username string, password string) (string, int) { //int是状态码
	expirationTime := time.Now().Add(5 * time.Minute) //token过期时间

	SetClaims := MyClaims{
		username,
		password,
		jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(), //是个int所有要给个时间戳
			Issuer:    "jinblog",
		},
	}

	ReqClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, SetClaims) //传入SetClaims因为要有两个入参是一个token的指针,方法是HS256加盐
	token, err := ReqClaims.SignedString(JwtKey)
	if err != nil {
		return "", errmsg.ERROR
	}
	return token, errmsg.SUCCESS
}

//验证token

func CheckToken(token string) (*MyClaims, int) { //返回一个MyClaims的指针
	setToken, _ := jwt.ParseWithClaims(token, &MyClaims{}, func(token *jwt.Token) (interface{}, error) { //ParseWithClaims是jwt所提供的方法
		return JwtKey, nil //用私钥解码
	}) //参数的地址是MyClaims
	if key, _ := setToken.Claims.(*MyClaims); setToken.Valid {
		return key, errmsg.SUCCESS
	} else {
		return nil, errmsg.ERROR
	}

}

//jwt中间件（控制验证写入router）

func JwtToken() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenHeader := c.Request.Header.Get("Authorization") //固定写法
		code := errmsg.SUCCESS
		if tokenHeader == "" { //判断如果是
			code = errmsg.ERROR_TOKEN_EXIST
			c.JSON(http.StatusOK, gin.H{
				"code":    code,
				"message": errmsg.GetErrMsg(code),
				//返回token中的信息
			})
			c.Abort()
			return
		}
		checkToken := strings.SplitN(tokenHeader, " ", 2)
		if len(checkToken) != 2 || checkToken[0] != "Bearer" { //固定写法
			code = errmsg.ERROR_TOKEN_FORMAT //token格式错误
			c.Abort()                        //用于立即终止当前的 HTTP 请求处理
			return
		}
		key, tCode := CheckToken(checkToken[1]) //传入checkToken的第二个参数进行比对
		if tCode != errmsg.SUCCESS {
			code = errmsg.ERROR_TOKEN_WRONG
			c.Abort()
			return
		}
		if time.Now().Unix() > key.ExpiresAt { //加入时间戳如果大于当地保存时间则过期
			code = errmsg.ERROR_TOKEN_TIMEOUT
			c.Abort() //如果token过期
			return
		}

		c.Set("username", key.Username) //绑定到username
		c.Next()                        //继续向下匹配
	}

}
